package br.gov.cemaden.imagefilter.filter;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;

public final class ApplyFilter {

    private Path source, target;
    private String filename;
    private boolean validPath, validFile;
    private Logger logger = Logger.getLogger(ApplyFilter.class.getName());

    /**
     * 
     * @param pathSource
     * @param pathTarget
     * @param file
     * @param outputFile 
     */
    public ApplyFilter(String source, String pathTarget, String filename) {        
        /* The output filename */
        this.filename = filename;        
        /* validates and assigns values to source and target attributes */
        this.validPath = validatePaths( Paths.get(source), Paths.get(pathTarget) );            
        this.validFile = validateSourceFile();        
    }
    
    /**
     * 
     * @param source
     * @param target
     * @return 
     */
    private boolean validatePaths(Path source, Path target){
        
        if(!Files.exists(source)){
            logger.error("The source directory doesn't exists.");
            return false;
        }
        if(!Files.exists(target)){
            logger.error("The target directory doesn't exists.");
            return false;
        }
        
        this.source = source;
        this.target = target;
        
        return true;
    }
    
    /**
     * 
     */
    private boolean validateSourceFile(){
        
        Pattern pattern;
        Matcher matcher;
        String fileSource = source.getFileName().toString();
        String date = new String();
        String hour = new String();
        /**
         * Search for 8 digit pattern in fileInput
         */
        pattern = Pattern.compile("[0-9]{8}");
        matcher = pattern.matcher(fileSource);
        if(matcher.find()){
            date = matcher.group();
        }
        
        /**
         * Search for 4 digit pattern in fileInput, preceded and succeeded by non-digit characters
         * (?<!\d) - Previous character is not a digit
         * ([0-9]{4}) - Is composed of four digits
         * (?!\d) - Next character is not a digit
         */
        pattern = Pattern.compile("(?<!\\d)([0-9]{4})(?!\\d)");
        matcher = pattern.matcher(fileSource);
        if(matcher.find()){
            hour = matcher.group();
        }
        
        /* If the filename is not valid, delete the file to prevent trying to filter it. */
        if( date.isEmpty() || hour.isEmpty() ){
                     
            logger.error("There's an error in the name of the input file - " + fileSource);
            
            deleteFile(source);                
            
            return false;
        }        
            
        this.setFilename(date, hour);
        return true;
    }
    
    private boolean deleteFile( Path path ){
        if( path != null ){
            try {
                if(Files.deleteIfExists(path))
                    return true;
            } catch (IOException ex) {                
                logger.error("The file " + path.toString() + " can not be deleted." 
                        + ex.getMessage());
            }
        }        
        return false;
    }
    
    /**
     * 
     * @param date
     * @param hour 
     */
    private void setFilename( String date, String hour ){
        filename = filename + "_" + date + hour + "00";
    }
    
    /**
     * 
     * @return 
     */
    public boolean isValid(){
        if(validPath && validFile)
            return true;
        return false;
    }

    /**
     * 
     * @return 
     */
    
    public boolean applyTransparency() {
        
        if(!isValid())
            return false;
        
        String fileSource = source.getFileName().toString();
        String extension = fileSource.toString().substring(fileSource.lastIndexOf(".") + 1, fileSource.length());
        
        BufferedImage image;
        BufferedImage resultImage;
        Image transpImg;
        
        /* Try to read the image */
        try{
            image = ImageIO.read(source.toFile());
        }
        catch(IOException ex){
            logger.error("Error when trying to read the file " + source.toString() 
                    + ex.getMessage());            
            if(deleteFile(source))
                logger.error("The file " + source.toString() + " was deleted.");
            return true;
        }
        
        transpImg = TransformColorToTransparency(image, new Color(255, 255, 255), new Color(255, 255, 255));
        resultImage = ImageToBufferedImage(transpImg, image.getWidth(), image.getHeight());
        
        image.flush();
        image = null;
        
        /* Try to create the image */
        try {                                                        
            ImageIO.write(resultImage, "png", new File(target.toString(), filename + ".png"));                

            logger.info("applyTransparency() - " + "Arquivo: " + filename 
                    + " - Extensão: " + extension 
                    + System.getProperty("line.separator")
                    + " - Source path: " + source.toString() 
                    + " - Target path: " + target.toString());
                                
            deleteFile(source);                     
            
        } catch (IOException e) {
            logger.error("Erro durante a tentativa de criar o arquivo: " + target.toString() + "/" + filename + ".png" 
                    + e.getMessage(), e.getCause());
            return false;
        }
        
        transpImg.flush();
        transpImg = null;
        resultImage.flush();
        resultImage = null;
        
        return true;
    }

    public boolean justSaveImageWithRGB() {
        
        if(!isValid())
            return false;
        
        String extension;
        File inFile;
        BufferedImage input, output;
            
        try {            
            
            inFile = new File(source.toString());
            
            input = ImageIO.read(inFile);            
            output = ImageToBufferedImage(input, input.getWidth(), input.getHeight());
            
            // Get file extension based on File Object getName() method
            extension = inFile.getName().substring(inFile.getName().lastIndexOf(".") + 1, inFile.getName().length());
            extension = extension.toLowerCase().trim();            
            
            // Create new image using ImageIO.write() method based on the file extension.
            
            if (extension.equals("jpg")) {
               
                ImageIO.write(output, "jpg", new File(target.toString(), source.getFileName().toString() + ".jpg"));                
                // Log
                logger.info("Created file " + source.getFileName().toString() + ".jpg in " + target.toString());
                
            } 
            else if(extension.equals("png")) {
                
                ImageIO.write(output, "png", new File(target.toString(), source.getFileName().toString() + ".png"));
                // Log
                logger.info("Created file " + source.getFileName().toString() + ".png in " + target.toString());
                
            }           
            else if (extension.equals("gif")) {
                
                // If .gif, save as .png
                
                ImageIO.write(output, "png", new File(target.toString() + source.getFileName().toString() + ".png"));                
                // Log
                logger.info("Created file " + source.getFileName().toString() + ".jpg in " + target.toString());                

                Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
                // Log
                logger.info("Moved file " + source.toString() + " to " + target.toString());                
            }
            
            logger.info("=> justSaveImageWithRGB()" + "Arquivo: " + source.getFileName().toString() 
                        + " - Extensão: " + extension + System.getProperty("line.separator")
                        + " - SourcePath: " + source.toString() 
                        + " - TargetPath: " + target.toString());
            
            return true;
            
        } 
        catch (IOException e) {
            logger.error("Erro no justSaveImageWithRGB(): " + e.getMessage(), e.getCause());
            return false;
        }
        finally {
            extension = null;
            inFile = null;
            input = null;
            output = null;
        }
    }

    /**
     * 
     * @param BufferedImage image
     * @return Image
     */
    private Image TransformGrayToTransparency(BufferedImage image) {
        
        ImageFilter filter = new RGBImageFilter() {

            @Override
            public final int filterRGB(int x, int y, int rgb) {
                return (rgb << 8) & 0xFF000000;
            }
        };
        
        return Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), filter));
    }

    /**
     * 
     * @param image
     * @param c1
     * @param c2
     * @return 
     */
    private Image TransformColorToTransparency(BufferedImage image, Color c1, Color c2) {
        
        // Primitive test, just an example
        final int r1 = c1.getRed();
        final int g1 = c1.getGreen();
        final int b1 = c1.getBlue();
        final int r2 = c2.getRed();
        final int g2 = c2.getGreen();
        final int b2 = c2.getBlue();
        
        ImageFilter filter = new RGBImageFilter() {

            @Override
            public final int filterRGB(int x, int y, int rgb) {
                int r = (rgb & 0xFF0000) >> 16;
                int g = (rgb & 0xFF00) >> 8;
                int b = rgb & 0xFF;
                if ( ( r >= r1 && r <= r2 )
                        && ( g >= g1 && g <= g2 )
                        && ( b >= b1 && b <= b2 ) ) {
                    // Set fully transparent but keep color
                    return rgb & 0xFFFFFF;
                }
                return rgb;
            }
        };
        
        return Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), filter));
    }

    /**
     * 
     * @param image
     * @param width
     * @param height
     * @return BufferedImage
     */
    private BufferedImage ImageToBufferedImage(Image image, int width, int height) {
        
        BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = dest.createGraphics();
        g2.drawImage(image, 0, 0, null);
        g2.dispose();
        return dest;
    }

}
